#include "mcleaner.hpp"

IMPLEMENT_APP(MCleanApp)

bool MCleanApp::OnInit()
{
  //call default initialization
  if (!wxApp::OnInit())
    return false;

  MyFrame *frame = new MyFrame(wxT("Modpack Cleaner"), wxSize(HORSIZE,VERSIZE), app_StartMod);
  frame->Show(true);
  SetTopWindow(frame);
  return true;
}

void MCleanApp::OnInitCmdLine(wxCmdLineParser &parser)
{
  parser.SetDesc (g_cmdLineDesc);
  parser.SetSwitchChars (wxT("-")); //not supporting "/", silly Windows
}

bool MCleanApp::OnCmdLineParsed(wxCmdLineParser &parser)
{
  parser.Found(wxT("mod"), &app_StartMod);

  wxArrayString files;
  for(unsigned int i = 0; i < parser.GetParamCount(); i++)
    files.Add(parser.GetParam(i));

  return true;
}

MyFrame::MyFrame(const wxString &title, const wxSize &mainsize, wxString StartMod)
  : wxFrame(NULL, -1, title, wxDefaultPosition, mainsize)
{
  findboxmode = MODE_FX;

  // set up starting path explicitly because VCExpress isn't very good at Cwd things
  wxFileName StartFull (wxStandardPaths::Get().GetExecutablePath());
  if (!StartMod.IsEmpty())
    StartFull.AppendDir (StartMod);

#ifdef WIN32 // I'm not guaranteeing this to work - FSF
  SetIcon(wxICON(mcleaner));
#endif

  // Fill up menu bar
  menuFile = new wxMenu;
  menuBar = new wxMenuBar;

  menuFile->Append(ID_CLEAR, wxT("&Clear\tAlt-C"), wxT("Clear all data"));
  menuFile->Append(ID_About, wxT("&About\tAlt-A"), wxT("About Modpack Cleaner"));
  menuFile->AppendSeparator();
  menuFile->Append(ID_Quit, wxT("E&xit\tCtrl-Q"), wxT("Exit Modpack Cleaner"));

  menuBar->Append(menuFile, wxT("&File"));
  SetMenuBar(menuBar);

  // Set up main window
  MainPanel  = new wxPanel(this);
  VertiSizer = new wxGridSizer(1,3,0,0);
  MainPanel->SetSizer(VertiSizer);

  ExpandFlags = new wxSizerFlags(1);
  ExpandFlags->Expand().Border(wxALL, 10);
  FixedFlags = new wxSizerFlags(1);
  FixedFlags->Border(wxALL,10);

  Dirpanel =
    new wxGenericDirCtrl(MainPanel, wxID_ANY, StartFull.GetPath(), 
                         wxDefaultPosition, wxDefaultSize,
                         wxDIRCTRL_DIR_ONLY);
  Miscgrid = new wxFlexGridSizer(4,1,0,0);
  Logpanel =
    new wxTextCtrl(MainPanel, wxID_ANY, wxEmptyString,
                   wxDefaultPosition, wxDefaultSize,
                   wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);

  VertiSizer->Add(Dirpanel, *ExpandFlags);
  VertiSizer->Add(Miscgrid, *ExpandFlags);
  VertiSizer->Add(Logpanel, *ExpandFlags);

  // Set up Miscgrid
  Miscgrid->SetFlexibleDirection(wxVERTICAL);
  Miscgrid->AddGrowableCol(0);
  Miscgrid->AddGrowableRow(2);

  do_curmod = new wxButton(MainPanel, ID_CHECKTHIS, wxT("Check selected mod"));
  Miscgrid->Add(do_curmod, *FixedFlags);

  do_outfile = new wxButton(MainPanel, ID_OUTFILE, wxT("Write output to file"));
  Miscgrid->Add(do_outfile, *FixedFlags);

  // Put items in FindBox
  FindBox = new wxStaticBoxSizer(wxVERTICAL, MainPanel, wxT("See where a file is used"));
  Miscgrid->Add(FindBox, *ExpandFlags);

  ModeSizer = new wxGridSizer(1,2,10,10);
  FindBox->Add(ModeSizer, wxSizerFlags(0).Border(wxALL,10));

  ModeFX = new wxRadioButton(MainPanel, ID_MODE_FX, wxT("Effects"),
                             wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
  ModeSizer->Add(ModeFX);

  ModeMP = new wxRadioButton(MainPanel, ID_MODE_MP, wxT("Maps"));
  ModeSizer->Add(ModeMP);

  FindAsset = new wxListCtrl(MainPanel, ID_SELECTED,
                             wxDefaultPosition, wxDefaultSize,
                             wxLC_LIST | wxLC_SINGLE_SEL);
  FindAsset->SetToolTip(wxT("Select a map or effect in your mod folder"));
  FindBox->Add(FindAsset, *ExpandFlags);

  Usedpane =
    new wxTextCtrl(MainPanel, wxID_ANY, wxT("< No file selected >"),
                   wxDefaultPosition, wxDefaultSize,
                   wxTE_READONLY | wxTE_MULTILINE | wxTE_DONTWRAP);
  Usedpane->SetToolTip(wxT("The map/effect selected above is used by these files"));
  FindBox->Add(Usedpane, *ExpandFlags);

  reset(); // set window contents to defaults, disable buttons etc
}

void MyFrame::OnClear (wxCommandEvent &WXUNUSED(event))
{
  reset();
}
void MyFrame::OnAbout (wxCommandEvent &WXUNUSED(event))
{
  wxAboutDialogInfo info;
  wxString desc = wxT("Lists potentially unused maps and effects in a modpack. Compiled with ");

  info.SetName(wxT("Modpack Cleaner"));
  info.SetVersion(MCVERSION);
  info.SetDescription(desc + wxVERSION_STRING + wxT("."));
  info.SetCopyright(wxT("(C) 2011-2013 FreeSpaceFreak @ Hard-Light Productions"));
  info.SetLicense(wxString::FromAscii("This program is free software. You can redistribute it and/or modify it under the terms of\nthe GNU General Public License as published by the Free Software Foundation,\neither version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\nwithout even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\nSee the GNU General Public License ( http://www.gnu.org/licenses/ ) for more details."));

  wxAboutBox(info);
}

void MyFrame::OnQuit(wxCommandEvent &WXUNUSED(event))
{
  Close(true);
}

void MyFrame::OnSelected(wxListEvent &event)
{
  // write list of files using selected file to Usedpane

  Usedpane->Clear();
  std::vector <wxString> Users = std::vector<wxString> ();

  if (findboxmode == MODE_MP)
    {
      Users = AllMaps[static_cast<int>(event.GetIndex())].getUsers();
    }
  else if (findboxmode == MODE_FX)
    {
      Users = AllFX[static_cast<int>(event.GetIndex())].getUsers();
    }

  if (Users.size() == 0)
    {
      Usedpane->AppendText(wxT("< Unused file >"));
    }
  else
    {
      for (std::vector<wxString>::const_iterator iter = Users.begin();
           iter != Users.end(); iter++)
        {
          Usedpane->AppendText(*iter);
          Usedpane->AppendText(wxT("\n"));
        }
    }
}

void MyFrame::OnModeFX(wxCommandEvent &WXUNUSED(event))
{
  findboxmode = MODE_FX;
  writeFindAsset(AllFX);
}

void MyFrame::OnModeMP(wxCommandEvent &WXUNUSED(event))
{
  findboxmode = MODE_MP;
  writeFindAsset(AllMaps);
}

// Main function, this is where stuff happens
void MyFrame::OnCheckThis(wxCommandEvent &WXUNUSED(event))
{
  
  do_curmod       -> Disable();
  do_outfile      -> Disable();
  Dirpanel        -> Disable();
  
  // Remove leftovers
  reset();
 
  // Init progress dialog
  wxProgressDialog *bardlg =
    new wxProgressDialog(wxT("Checking mod..."),
                         wxT("Preparing to read mod folder..."));

  try
    {
      //DECLARATION

      FileReader ReadThis; //Will parse model & table files.
      wxString strentry; //temporary storage of strings
      bool dir_changed = false; //directory change success?

      bool confound   = false;
      bool effecfound = false;
      bool mapfound   = false;
      bool missionfound = false;
      bool modelfound = false;
      bool tablefound = false;

      datadir.AssignDir(Dirpanel->GetPath());
      wxFileName condir;
      wxFileName effecdir;
      wxFileName mapdir;
      wxFileName missiondir;
      wxFileName modeldir;
      wxFileName tabledir;

      // EXECUTION
      datadir.AppendDir(wxT("data"));
      wxDir dir(datadir.GetPath());

      bardlg->Update(5, wxT("Looking for mod directories..."));

      if (!dir.IsOpened() )
        throw 1;

      bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_DIRS);
      while (cont)
        {
          if (strentry.Lower() == wxT("config"))
            {
              condir.Assign(datadir);
              condir.AppendDir(strentry); //use original name for case-sensitivity
              confound = true;
            }
          else if (strentry.Lower() == wxT("effects"))
            {
              effecdir.Assign(datadir);
              effecdir.AppendDir(strentry);
              effecfound = true;
            }
          else if (strentry.Lower() == wxT("maps"))
            {
              mapdir.Assign(datadir);
              mapdir.AppendDir(strentry);
              mapfound = true;
            }
          else if (strentry.Lower() == wxT("missions"))
            {
              missiondir.Assign(datadir);
              missiondir.AppendDir(strentry);
              missionfound = true;
            }
          else if (strentry.Lower() == wxT("models"))
            {
              modeldir.Assign(datadir);
              modeldir.AppendDir(strentry);
              modelfound = true;
            }
          else if (strentry.Lower() == wxT("tables"))
            {
              tabledir.Assign(datadir);
              tabledir.AppendDir(strentry);
              tablefound = true;
            }

          cont = dir.GetNext(&strentry);
        }

      //inventorize effects

      if (effecfound)
        {
          dir_changed = wxSetWorkingDirectory(effecdir.GetPath().c_str());//set working directory
          if (dir_changed && (dir.Open(effecdir.GetPath().c_str())))
            {
              bardlg->Update(10, wxT("Inventorizing effects..."));
              bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_FILES);
              while (cont)
                {
                  if (strentry.Lower().rfind(wxT(".sdr")) != strentry.Lower().size()-4)
                    { //skip shaders
                      AllFX.push_back(strentry);
                    }
                  cont = dir.GetNext(&strentry);
                }
            }
        }
      else
        {
          (*Logpanel) << wxT("Unable to find effects directory.\n");
        }

      //inventorize maps
      if (mapfound)
        {
          dir_changed = wxSetWorkingDirectory(mapdir.GetPath().c_str());//set working directory
          if (dir_changed && (dir.Open(mapdir.GetPath().c_str())))
            {
              bardlg->Update(20, wxT("Inventorizing maps..."));
              bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_FILES);
              while (cont)
                {
                  AllMaps.push_back(strentry);
                  cont = dir.GetNext(&strentry);

                }
            }
        }
      else
        {
          (*Logpanel) << wxT("Unable to find maps directory.\n");
        }

      //read config files

      if (confound)
        {
          dir_changed = wxSetWorkingDirectory(condir.GetPath()); //set working directory
          if (dir_changed && (dir.Open(condir.GetPath())))
            {
              bardlg->Update(30, wxT("Reading config directory..."));
              bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_FILES);
              while (cont)
                {
                  if (ReadThis.openFile(strentry))
                    {
                      // Not supporting maps in config files
                      addEffectUser(strentry, ReadThis.getFX());
                    }
                  else
                    {
                      (*Logpanel) << wxT(" - Skipping ") << strentry << wxT("\n");
                    }
                  cont = dir.GetNext(&strentry);
                }
            }
        }
      else
        {
          (*Logpanel) << wxT("Unable to find config directory.\n");
        }

      //read missions

      if (missionfound)
        {
          dir_changed = wxSetWorkingDirectory(missiondir.GetPath()); 
          if (dir_changed && dir.Open(missiondir.GetPath()))
            {
              bardlg->Update(35, wxT("Reading missions..."));
              bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_FILES);
              while (cont)
                {
                  if (ReadThis.openFile(strentry))
                    {
                      addMapUser(strentry, ReadThis.getMaps());
                      addEffectUser(strentry, ReadThis.getFX());
                    }
                  else
                    {
                      (*Logpanel) << wxT(" - Skipping ") << strentry << wxT("\n");
                    }
                  cont = dir.GetNext(&strentry);
                }
            }
        }

      else
        {
          (*Logpanel) << wxT("Unable to find missions directory.\n");
        }

      //look for model maps & glowpoints
      if (modelfound)
        {
          dir_changed = wxSetWorkingDirectory(modeldir.GetPath()); 
          if (dir_changed && dir.Open(modeldir.GetPath()))
            {
              bardlg->Update(40, wxT("Reading models..."));
              bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_FILES);
              while (cont)
                {
                  if (ReadThis.openFile(strentry))
                    {
                      addMapUser(strentry, ReadThis.getMaps());
                      addEffectUser(strentry, ReadThis.getFX());
                    }
                  else
                    {
                      (*Logpanel) << wxT(" - Skipping ") << strentry << wxT("\n");
                    }
                  cont = dir.GetNext(&strentry);
                }
            }
        }

      else
        {
          (*Logpanel) << wxT("Unable to find models directory.\n");
        }

      //read tables
      if (tablefound)
        {
          dir_changed = wxSetWorkingDirectory(tabledir.GetPath()); 
          if (dir_changed && dir.Open(tabledir.GetPath()))
            {
              bardlg->Update(45, wxT("Reading tables..."));
              bool cont = dir.GetFirst(&strentry, wxEmptyString, wxDIR_FILES);
              while (cont)
                {
                  if (ReadThis.openFile(strentry))
                    {
                      addMapUser(strentry, ReadThis.getMaps());
                      addEffectUser(strentry, ReadThis.getFX());
                    }
                  else
                    {
                      (*Logpanel) << wxT(" - Skipping ") << strentry << wxT("\n");
                    }
                  cont = dir.GetNext(&strentry);
                }
            }
        }

      else
        {
          (*Logpanel) << wxT("Unable to find table directory.\n");
        }

      // Sort AllMaps & AllFX for user-friendliness
      bardlg->Update(50, wxT("Sorting..."));
      sort (AllFX.begin(), AllFX.end(), compareAssets);
      sort (AllMaps.begin(), AllMaps.end(), compareAssets);

      wxString corename; //Avoid excessive iter1->getCore() calls

      bardlg->Update(60, wxT("Checking which maps are in use..."));
      for (std::vector<Asset>::iterator iter1 = AllMaps.begin();
           iter1 != AllMaps.end(); iter1++)
        {
          corename = iter1->getCore();

          //see if hard-coded...
          if (corename == wxT("cloakmap")
              || corename == wxT("insignia")
              || corename == wxT("warpball01")
              || corename == wxT("warpglow01")
              )
            {
              iter1->addUser(wxT("< Hard-coded file >"));
            }

          //... then see if in use
          if (iter1->isUnused())
            {
              unusedmaps.push_back(iter1->getOrig());
            }
        }
      
      bardlg->Update(80, wxT("Checking which effects are in use..."));

      for (std::vector<Asset>::iterator iter1 = AllFX.begin();
           iter1 != AllFX.end(); iter1++)
        {
          corename = iter1->getCore();

          //see if hard-coded...
          if (corename == wxT("cubemap")
              || corename == wxT("gamepalette1-01")
              || corename == wxT("particleexp01")
              || corename == wxT("particlesmoke01")
              || corename == wxT("particlesmoke02")
              || corename == wxT("shockwave01")
              || corename == wxT("thruster01")
              || corename == wxT("thruster01a")
              || corename == wxT("thruster02")
              || corename == wxT("thruster02a")
              || corename == wxT("thruster03")
              || corename == wxT("thruster03a")
              || corename == wxT("thrusterglow02")
              || corename == wxT("thrusterglow02a")
              || corename == wxT("warpglow01") )
            {
              iter1->addUser(wxT("< Hard-coded file >"));
            }
          //... then see if in use
          if (iter1->isUnused())
            {
              unusedfx.push_back(iter1->getOrig());
            }
        }

      //output to log pane
      int nunused = unusedmaps.size();

      (*Logpanel) << wxT("\nUnused maps: ") << nunused << wxT("\n");
      if (nunused != 0)
        {
          for (std::vector<wxString>::const_iterator iter1 = unusedmaps.begin();
               iter1 != unusedmaps.end(); iter1++)
            {
              (*Logpanel) << *iter1 << wxT("\n");
            }
        }


      nunused = unusedfx.size();
      (*Logpanel) << wxT("\nUnused effects: ") << nunused << wxT("\n");
      if (nunused != 0)
        {
          for (std::vector<wxString>::const_iterator iter1 = unusedfx.begin();
               iter1 != unusedfx.end(); iter1++)
            {
              (*Logpanel) << *iter1 << wxT("\n");
            }
        }

      // Write selected assets to FindAsset pane
      if (findboxmode == MODE_FX)
        {
          writeFindAsset(AllFX);
        }
      else if (findboxmode == MODE_MP)
        {
          writeFindAsset(AllMaps);
        }
      do_outfile      -> Enable();
      ModeFX          -> Enable();
      ModeMP          -> Enable();
      FindAsset       -> Enable();
      Usedpane        -> Enable();
   }
  catch (int e)
    {
      switch (e)
        {
        case 1:
          (*Logpanel) << wxT("Error reading mod data directory.\n");
          break;
        default:
          (*Logpanel) << wxT("Error ") << e << wxT(" occurred. Please alert FSF.\n");
        }
    }
  catch (...)
    {
      (*Logpanel) << wxT("Something _really_ weird happened. What have you been doing to my program?\n");
    }
  
  do_curmod       -> Enable();
  Dirpanel        -> Enable();
  
  bardlg->Destroy();
}

void MyFrame::OnOutfile(wxCommandEvent &WXUNUSED(event))
{
  try
    {
      if (!datadir.IsOk())
        throw 2;

      wxSetWorkingDirectory(datadir.GetPath()); 
      std::ofstream OutFile;
      OutFile.open("unused_assets.txt");

      if (!OutFile.is_open())
        throw 1;

      else
        {
          OutFile << "Modpack Cleaner " << MCVERSION.mb_str() << std::endl;
          //maps
          int nunused = unusedmaps.size();
          OutFile << "\nUnused maps: " << nunused << std::endl;
          if (nunused != 0)
            {
              for (std::vector<wxString>::const_iterator iter1 = unusedmaps.begin();
                   iter1 != unusedmaps.end(); iter1++)
                {
                  OutFile << iter1->mb_str() << std::endl;
                }
            }

          //effects
          nunused = unusedfx.size();
          OutFile << "\nUnused effects: " << nunused << std::endl;
          if (nunused != 0)
            {
              for (std::vector<wxString>::const_iterator iter1 = unusedfx.begin();
                   iter1 != unusedfx.end(); iter1++)
                {
                  OutFile << iter1->mb_str() << std::endl;
                }
            }
          OutFile.close();
          (*Logpanel) << wxT("\nOutput written to ") << datadir.GetPathWithSep() << wxT("unused_assets.txt.\n");

        }
    }
  catch (int e)
    {
      switch (e)
        {
        case 1:
          (*Logpanel) << wxT("Error creating output file. Does the program have write permission?\n");
          break;
        case 2:
          (*Logpanel) << wxT("No data to write. This is not supposed to happen.\n");
          (*Logpanel) << wxT("Please tell FSF he messed up case 2.\n");
          break;
        }
    }
  catch (...)
    {
      (*Logpanel) << wxT("If you're seeing this, something went _seriously_ wrong.\n");
      (*Logpanel) << wxT("Please report to FreeSpaceFreak.\n");
    }
}

void MyFrame::addEffectUser(wxString User, std::vector <wxString> Using)
{
  if (Using.size() == 0)
    return;

  for (std::vector<wxString>::const_iterator iter1 = Using.begin();
       iter1 != Using.end(); iter1++)
    {
      for (std::vector<Asset>::iterator iter2 = AllFX.begin();
           iter2 != AllFX.end(); iter2++)
        {
          if ((*iter1) == (*iter2).getCore())
            {
              (*iter2).addUser(User);
            }
        }
    }
  return;
}

void MyFrame::addMapUser(wxString User, std::vector <wxString> Using)
{
  if (Using.size() == 0)
    return;

  for (std::vector<wxString>::const_iterator iter1 = Using.begin();
       iter1 != Using.end(); iter1++)
    {
      for (std::vector<Asset>::iterator iter2 = AllMaps.begin();
           iter2 != AllMaps.end(); iter2++)
        {
          if ((*iter1) == (*iter2).getCore())
            {
              (*iter2).addUser(User);
            }
        }
    }
  return;
}

void MyFrame::writeFindAsset(std::vector <Asset> WriteThis)
{
  FindAsset->ClearAll();
  long i = 0;

  for(std::vector <Asset>::const_iterator iter1 = WriteThis.begin();
      iter1 != WriteThis.end(); iter1++)
    {
      (*FindAsset).InsertItem(i++, (*iter1).getOrig());
    }
}

void MyFrame::reset()
{
  AllMaps.clear();
  AllFX.clear();
  unusedmaps.clear();
  unusedfx.clear();
  datadir.Clear();

  Logpanel->Clear();
  FindAsset->ClearAll();

  Usedpane->Clear();
  Usedpane->AppendText(wxT("< No file selected >"));

  ModeFX          -> Disable();
  ModeMP          -> Disable();
  FindAsset       -> Disable();
  Usedpane        -> Disable();
  do_outfile      -> Disable();
  
}

// case- & extension-insensitive comparison for sorting AllMaps, AllFX
bool compareAssets (Asset One, Asset Two)
{
  bool returnthis = One.getComp() < Two.getComp();
  return (returnthis);
}
